package beautiful.butterfly.schedule.task_manager;

import beautiful.butterfly.schedule.TaskItemAndParameter;
import beautiful.butterfly.schedule.strategy.ScheduleFactory;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.data.Stat;

import java.util.List;
import java.util.Map;

@Slf4j
public class ScheduleManager extends AbstractScheduleManager {

    @Setter
    @Getter
    private final Object needTaskItemLock = new Object();
    @Setter
    @Getter
    protected int taskItemCount = 0;
    @Setter
    @Getter
    protected long lastFetchVersion = -1;

    public ScheduleManager(ScheduleFactory scheduleFactory,//
                           String baseTaskType,//
                           String ownSign, //
                           IDataManager dataManager) throws Exception//
    {
        super(scheduleFactory, baseTaskType, ownSign, dataManager);
    }

    public void createTaskItems() throws Exception {
        dataManager.clearExpireServers(this.taskHandler.getTaskType(), this.taskType.getJudgeDeadInterval());
        List<String> serverIdList = dataManager.loadServerIdList(this.taskHandler.getTaskType());
        if (dataManager.isLeaderServerId(this.taskHandler.getServerId(), serverIdList)) {
            log.debug(this.taskHandler.getServerId() + ":" + serverIdList.size());
            this.dataManager.createTaskItems(this.taskHandler.getBaseTaskType(), this.taskHandler.getOwnSign(), this.taskHandler.getServerId());
        }
    }

    public void initial() throws Exception {
        new Thread(this.taskHandler.getTaskType() + "-" + this.currentSerialNumber + "-StartProcess") {
            @SuppressWarnings("static-access")
            public void run() {
                try {
                    log.info(" of " + taskHandler.getServerId());
                    while (isLeaderServer == false) {
                        if (isStopSchedule == true) {
                            log.debug("" + taskHandler.getServerId());
                            return;
                        }
                        try {
                            createTaskItems();
                            isLeaderServer = dataManager.isLeaderServer(taskHandler.getBaseTaskType(), taskHandler.getOwnSign());
                        } catch (Throwable e) {
                            log.error(e.getMessage(), e);
                        }
                        if (isLeaderServer == false) {
                            Thread.currentThread().sleep(1000);
                        }
                    }//end
                    int count = 0;
                    lastTaskItemListTime = dataManager.zooKeeperCurrentTimeMillis();
                    while (getTaskItemIdAndParameterList().size() <= 0) {
                        if (isStopSchedule == true) {
                            log.debug("" + taskHandler.getServerId());
                            return;
                        }
                        Thread.currentThread().sleep(1000);
                        count = count + 1;
                    }
                    String tmpStr = "TaskItemAndParameter:";
                    for (int i = 0; i < currentTaskItemAndParameterList.size(); i++) {
                        if (i > 0) {
                            tmpStr = tmpStr + ",";
                        }
                        tmpStr = tmpStr + currentTaskItemAndParameterList.get(i);
                    }
                    log.info("" + tmpStr + "  of  " + taskHandler.getServerId());

                    taskItemCount = dataManager.loadTaskItemList(taskHandler.getTaskType()).size();
                    computeStartDateTime();
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                    String str = e.getMessage();
                    if (str.length() > 300) {
                        str = str.substring(0, 300);
                    }
                    startErrorInfo = "" + str;
                }
            }//异步
        }.start();
    }

    public void refreshScheduleServerInfo() throws Exception {
        try {
            registerTaskHandler();
            if (this.isLeaderServer == false) {
                return;
            }

            this.assignScheduleTask();

            boolean needTaskItemList = this.isNeedGetTaskItemIdAndParameterList();
            if (needTaskItemList != this.isNeedTaskItemList) {
                synchronized (needTaskItemLock) {
                    this.isNeedTaskItemList = true;
                }
                registerTaskHandler();
            }

            if (this.isPauseSchedule == true || this.processor != null && processor.isSleeping() == true) {
                this.getTaskItemIdAndParameterList();
            }
        } catch (Throwable e) {
            this.clearTaskList();
            if (e instanceof Exception) {
                throw (Exception) e;
            } else {
                throw new Exception(e.getMessage(), e);
            }
        }
    }

    public boolean isNeedTaskItemList() {
        try {
            return this.lastFetchVersion < this.dataManager.getTaskItemStatVersion(this.taskHandler.getTaskType());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    private boolean isExistServer(String type, Map<String, Stat> serverIdToStatMap) throws Exception {
        boolean exist = false;
        for (String serverId : serverIdToStatMap.keySet()) {
            Stat stat = serverIdToStatMap.get(serverId);
            if (this.dataManager.zooKeeperCurrentTimeMillis() - stat.getMtime() > this.taskType.getHeartBeatRate() * 40) {
                log.error("zombie serverList exists! serv=" + serverId + " ,type=" + type + "");
                exist = true;
            }
        }
        return exist;

    }

    public void assignScheduleTask() throws Exception {
        dataManager.clearExpireServers(this.taskHandler.getTaskType(), this.taskType.getJudgeDeadInterval());
        List<String> serverList = dataManager
                .loadServerIdList(this.taskHandler.getTaskType());

        if (dataManager.isLeaderServerId(this.taskHandler.getServerId(), serverList) == false) {
            if (log.isDebugEnabled()) {
                log.debug(this.taskHandler.getServerId() + ":Leader");
            }
            return;
        }
        dataManager.setTaskItemsWithServerId(this.taskHandler.getBaseTaskType(), this.taskHandler.getTaskType(), this.taskHandler.getServerId());
        dataManager.createTaskItemIfNotFind(this.taskHandler.getTaskType(), serverList);
        dataManager.assignTaskItem(this.taskHandler.getTaskType(), this.taskHandler.getServerId(), this.taskType.getMaxTaskItemsOfOneThreadGroup(), serverList);
    }

    public List<TaskItemAndParameter> getCurrentTaskItemAndParameterList() {
        try {
            if (this.isNeedTaskItemList == true) {
                if (this.processor != null) {
                    while (this.processor.taskListIsAllFinshed() == false) {
                        Thread.sleep(50);
                    }
                }
                synchronized (needTaskItemLock) {
                    this.getTaskItemIdAndParameterList();
                    this.isNeedTaskItemList = false;
                }
            }
            this.lastTaskItemListTime = this.dataManager.zooKeeperCurrentTimeMillis();
            return this.currentTaskItemAndParameterList;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected synchronized List<TaskItemAndParameter> getTaskItemIdAndParameterList() throws Exception {
        try {
            Map<String, Stat> serverPathToStatMap = this.dataManager.getServerIdToStatMap(this.taskHandler.getTaskType());
            isExistServer(this.taskHandler.getTaskType(), serverPathToStatMap);
        } catch (Exception e) {
            log.error("zombie serverList exists Exception:", e);
        }
//
        this.lastFetchVersion = this.dataManager.getTaskItemStatVersion(this.taskHandler.getTaskType());
        log.debug(" this.taskHandler.getTaskHandlerTaskType()=" + this.taskHandler.getTaskType() + ",  need =" + isNeedTaskItemList);
        try {
            this.dataManager.releaseDealTaskItem(this.taskHandler.getTaskType(), this.taskHandler.getServerId());
            this.currentTaskItemAndParameterList.clear();
            this.currentTaskItemAndParameterList = this.dataManager.getTaskItemAndParameterList(
                    this.taskHandler.getTaskType(), this.taskHandler.getServerId());

            if (this.currentTaskItemAndParameterList.size() == 0 &&
                    dataManager.zooKeeperCurrentTimeMillis() - this.lastTaskItemListTime
                            > this.taskType.getHeartBeatRate() * 20) {
                StringBuffer sb = new StringBuffer();
                sb.append(this.taskHandler.getServerId());
                sb.append("[TASK_TYPE=");
                sb.append(this.taskHandler.getTaskType());
                sb.append("]");
                sb.append("  taskItemAndParameterList.size() =" + currentTaskItemAndParameterList.size());
                sb.append(" ,dataManager.zooKeeperCurrentTimeMillis()=" + dataManager.zooKeeperCurrentTimeMillis());
                sb.append(" ,lastUpdateTaskItemAndParameterListTime=" + lastTaskItemListTime);
                sb.append(" ,taskType.getHeartBeatRate()=" + taskType.getHeartBeatRate() * 10);
                log.warn(sb.toString());
            }

            if (this.currentTaskItemAndParameterList.size() > 0) {
                this.lastTaskItemListTime = dataManager.zooKeeperCurrentTimeMillis();
            }

            return this.currentTaskItemAndParameterList;
        } catch (Throwable e) {
            this.lastFetchVersion = -1;
            if (e instanceof Exception) {
                throw (Exception) e;
            } else {
                throw new Exception(e);
            }
        }
    }


}
